package cc.shacocloud.mirage.utils;

import cc.shacocloud.mirage.utils.charSequence.CharUtil;
import cc.shacocloud.mirage.utils.charSequence.StrUtil;
import cc.shacocloud.mirage.utils.collection.CollUtil;
import cc.shacocloud.mirage.utils.converter.TypeConverter;
import cc.shacocloud.mirage.utils.converter.TypeDescriptor;
import cc.shacocloud.mirage.utils.reflection.ReflectUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author 思追(shaco)
 */
public class ObjectUtil {
    
    /**
     * 获取指定对象嵌套路径的值，如果该嵌套路径不存在则抛出例外
     *
     * @param object     目标对象
     * @param nestedPath 嵌套路径，多级使用 {@link CharUtil#DOT} 分割，如果是集合可以通过 [0] 的方式来指定下标，例如：a.b[1]
     * @return 值 {@link Object}
     */
    @Nullable
    @SuppressWarnings({"rawtypes"})
    public static Object getNestedPathValue(@NotNull Object object, @NotNull String nestedPath) throws IllegalArgumentException {
        Object target = object;
        for (String fieldName : StrUtil.split(nestedPath, CharUtil.DOT, true, false)) {
            
            // 集合的嵌套索引
            List<Integer> nestedIndex = nestedIndex(fieldName);
            if (Objects.nonNull(nestedIndex)) {
                fieldName = StrUtil.subPre(fieldName, fieldName.indexOf("["));
            }
            
            // null
            if (Objects.isNull(target)) {
                break;
            }
            // Map
            else if (target instanceof Map) {
                target = ((Map) target).get(fieldName);
            }
            // Bean 对象
            else if (ClassUtil.isBean(target.getClass())) {
                Field field = ReflectUtil.getField(target.getClass(), fieldName);
                if (Objects.isNull(field)) {
                    throw new IllegalArgumentException();
                }
                target = ReflectUtil.getFieldValue(target, field);
            }
            // 未知类型
            else {
                throw new IllegalArgumentException(String.format("暂不支持类型的 %s 的嵌套路径取值", target.getClass()));
            }
            
            // 集合索引嵌套
            if (CollUtil.isNotEmpty(nestedIndex)) {
                target = getNestedIndexValue(target, nestedIndex);
            }
        }
        
        return target;
    }
    
    /**
     * 设置指定对象嵌套路径的值，如果该嵌套路径不存在则抛出例外
     *
     * @param object     目标对象
     * @param nestedPath 嵌套路径，多级使用 {@link CharUtil#DOT} 分割，如果是集合可以通过 [0] 的方式来指定下标，例如：a.b[1]
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static void setNestedPathValue(@NotNull Object object,
                                          @NotNull String nestedPath,
                                          @NotNull Object value,
                                          @NotNull TypeConverter typeConverter) throws IllegalArgumentException {
        
        Object target;
        String path;
        
        int dotLastIndexOf = nestedPath.lastIndexOf(StrUtil.DOT);
        int arrLastIndexOf = nestedPath.lastIndexOf("[");
        
        // 集合索引赋值
        if (dotLastIndexOf > -1 && arrLastIndexOf > dotLastIndexOf) {
            target = getNestedPathValue(object, StrUtil.subPre(nestedPath, arrLastIndexOf));
            
            // null
            if (Objects.isNull(target)) {
                throw new IllegalArgumentException(String.format("对象 %s 无法满足嵌套路径 %s 的赋值！", object, nestedPath));
            }
            
            path = Objects.requireNonNull(StrUtil.subSuf(nestedPath, arrLastIndexOf));
            String indexStr = path.replaceAll("[^0-9]", "");
            if (NumberUtil.isInteger(indexStr)) {
                int index = Integer.parseInt(indexStr);
                // List
                if (target instanceof List) {
                    List list = (List) object;
                    if (index >= list.size()) {
                        throw new IllegalArgumentException(String.format("集合索引越界 size=%s, index=%s", list.size(), index));
                    }
                    list.set(index, value);
                }
                // 数组
                else if (target.getClass().isArray()) {
                    Object[] array = (Object[]) target;
                    if (index >= array.length) {
                        throw new IllegalArgumentException(String.format("数组索引越界 length=%s, index=%s", array.length, index));
                    }
                    array[index] = value;
                } else {
                    throw new IllegalArgumentException(String.format("对象 %s 不是集合或者数组类型，无法进行赋值！", object));
                }
            } else {
                throw new IllegalArgumentException(String.format("无效的嵌套路径：%s", path));
            }
        }
        // 对象属性赋值
        else {
            target = dotLastIndexOf > -1 ? getNestedPathValue(object, StrUtil.subPre(nestedPath, dotLastIndexOf)) : object;
            
            // null
            if (Objects.isNull(target)) {
                throw new IllegalArgumentException(String.format("对象 %s 无法满足嵌套路径 %s 的赋值！", object, nestedPath));
            }
            
            path = Objects.requireNonNull(StrUtil.subSuf(nestedPath, dotLastIndexOf + 1));
            
            // Map
            if (target instanceof Map) {
                ((Map) target).put(path, value);
            }
            // Bean 对象
            else if (ClassUtil.isBean(target.getClass())) {
                Field field = ReflectUtil.getField(target.getClass(), path);
                if (Objects.isNull(field)) {
                    throw new IllegalArgumentException();
                }
                
                // 类型转换
                value = typeConverter.convertIfNecessary(value, new TypeDescriptor(field));
                ReflectUtil.setFieldValue(target, field, value);
            }
            // 未知类型
            else {
                throw new IllegalArgumentException(String.format("暂不支持类型的 %s 的嵌套路径取值", target.getClass()));
            }
        }
    }
    
    /**
     * 获取嵌套索引值
     *
     * @param object      目标对象
     * @param nestedIndex 索引
     * @return 结果值
     */
    private static Object getNestedIndexValue(@NotNull Object object, @NotNull List<Integer> nestedIndex) {
        Object target = object;
        for (Integer index : nestedIndex) {
            target = getIndexValue(target, index);
        }
        
        return target;
    }
    
    /**
     * 获取嵌套索引值
     *
     * @param object 目标对象
     * @param index  索引
     * @return 结果值
     */
    @SuppressWarnings({"rawtypes"})
    private static Object getIndexValue(@NotNull Object object, @NotNull Integer index) {
        Object target;
        // List
        if (object instanceof List) {
            List list = (List) object;
            if (index >= list.size()) {
                throw new IllegalArgumentException(String.format("集合索引越界 size=%s, index=%s", list.size(), index));
            }
            target = list.get(index);
        }
        // 数组
        else if (object.getClass().isArray()) {
            Object[] array = (Object[]) object;
            if (index >= array.length) {
                throw new IllegalArgumentException(String.format("数组索引越界 length=%s, index=%s", array.length, index));
            }
            target = array[index];
        } else {
            throw new IllegalArgumentException(String.format("对象 %s 不是集合或者数组类型，无法进行取值！", object));
        }
        return target;
    }
    
    /**
     * 获取路径嵌套索引，如果不存在则返回 null
     *
     * @param path 路径
     * @return 嵌套索引，如果不存在则返回 null
     */
    @Nullable
    private static List<Integer> nestedIndex(@NotNull String path) {
        List<Integer> nestedIndex = null;
        if (path.contains("[")) {
            List<String> split = StrUtil.split(path, '[', true, true);
            
            nestedIndex = new ArrayList<>();
            for (int i = 1; i < split.size(); i++) {
                String indexStr = split.get(i).replaceAll("[^0-9]", "");
                
                if (NumberUtil.isInteger(indexStr)) {
                    nestedIndex.add(Integer.parseInt(indexStr));
                } else {
                    throw new IllegalArgumentException(String.format("无效的嵌套路径：%s", path));
                }
            }
        }
        return nestedIndex;
    }
    
}
